var  b2Vec2 = Box2D.Common.Math.b2Vec2
	,b2BodyDef = Box2D.Dynamics.b2BodyDef
	,b2Body = Box2D.Dynamics.b2Body
	,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
	,b2Fixture = Box2D.Dynamics.b2Fixture
	,b2World = Box2D.Dynamics.b2World
	,b2MassData = Box2D.Collision.Shapes.b2MassData
	,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
	,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
	,b2AABB=Box2D.Collision.b2AABB
	,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
	,b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef
	,b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef
	,b2DistanceJointDef = Box2D.Dynamics.Joints.b2DistanceJointDef
	,b2GearJointDef = Box2D.Dynamics.Joints.b2GearJointDef
	,b2PulleyJointDef = Box2D.Dynamics.Joints.b2PulleyJointDef
	,b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef
	,b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef
	,b2LineJointDef = Box2D.Dynamics.Joints.b2LineJointDef
	,b2WheelJointDef = Box2D.Dynamics.Joints.b2WheelJointDef//轮子关节 此关节在Box2dWeb-2.1.a.3.min.js没有
	,b2MotorJointDef = Box2D.Dynamics.Joints.b2MotorJointDef//马达关节 此关节在Box2dWeb-2.1.a.3.min.js没有
	,b2RopeJointDef = Box2D.Dynamics.Joints.b2RopeJointDef	//绳索关节 此关节在Box2dWeb-2.1.a.3.min.js没有
	,b2BuoyancyController = Box2D.Dynamics.Controllers.b2BuoyancyController//模拟水里面
	;
//默认的一些属性
var moonConfig={
	wall:{friction:0.5,restitution:0.5}//默认墙属性,摩擦力,弹性系数s
	,fixDef:{friction:0.5,restitution:0.5,density:1}//默认刚体属性,摩擦力,弹性系数,密度
	,mouseMaxForce:300//鼠标最大力度
	,isShowDebuger:true//是否显示debuger
	,gravity:10//重力加速度
	,gravityX:0//左右作用力默认是0
	,wallScaleX:1//墙在x轴上的倍数
	,wallScaleY:1//墙在y轴上的倍数
	,isUpdateContext:true//是否更新canvas的context画布
}	
function log(...a){console.log(...a)}
/**

SetType(0)传入b2Body.b2_staticBody为静态，0,1,2静,可静可动,动

ApplyForce//在刚体上施加vector压力	
ApplyImpulse//为刚体添加速度
ApplyTorque//为刚体添加转动的力矩
SetLinearVelocity//为刚体添加线形速度

Box2D中控制一个刚体，让它乖乖的听我们的话，有三种方法：ApplyForce、ApplyImpulse和SetLinearVelocity。它们都是b2Body类的公共方法，而且它们都接收一个b2Vec2类型向量参数。 
1.力，循序渐进——ApplyForce
顾名思义，ApplyForce方法会在刚体上施加一个力。学过物理力学的同学都知道，F=ma，有了力F就有了加速度a，有了加速度，物体就会有速度，就会慢慢动起来。(但是不会立马动起来，因为力不会直接影响速度)。
举个简单的例子，小明推一个静止的箱子，箱子不会立马飞出去，而是慢慢的、越来越快的动起来(减速也一样)。
2.速度，叠加——ApplyImpulse
与ApplyForce不同，ApplyImpulse不会产生力，而是直接影响刚体的速度。通过ApplyImpulse方法添加的速度会与刚体原有的速度叠加，产生新的速度。
3.一触即发——SetLinearVelocity
setLinearVelocity与ApplyImpulse一样，直接影响刚体的速度。不一样的是，setLinearVelocity添加的速度会覆盖刚体原有的速度。不过，在SetLinearVelocity方法不会自动唤醒sleeping的刚体，所以在调用该方法之前，记得将刚体body.setAwake(true);一下

关节joint：关节可以对两个或多个物体进行一种约束。
JointBase->Component 关节基类
DistanceJoint 距离关节：两个物体上面各自有一点，两点之间的距离固定不变
GearJoint 齿轮关节：用来模拟两个齿轮间的约束关系，齿轮旋转时，产生的动量有两种输出方式，一种是齿轮本身的角速度，另一种是齿轮表面的线速度
MotorJoint 马达发动机关节：用来限制两个刚体，使其相对位置和角度保持不变
MouseJoint 鼠标关节：鼠标关节用于通过鼠标来操控物体。它试图将物体拖向当前鼠标光标的位置。而在旋转方面就没有限制。
PrismaticJoint 平移关节：移动关节允许两个物体沿指定轴相对移动，它会阻止相对旋转
PulleyJoint 滑轮关节：它将两个物体接地(ground)并彼此连接，当一个物体上升，另一个物体就会下降
RevoluteJoint 旋转关节：旋转关节强制两个物体共享一个锚点，两个物体相对旋转
RopeJoint 绳索关节：限制了两个点之间的最大距离。它能够阻止连接的物体之间的拉伸，即使在很大的负载下
WeldJoint 焊接关节：焊接关节的用途是使两个物体不能相对运动，受到关节的限制，两个刚体的相对位置和角度都保持不变，看上去像一个整体
WheelJoint 轮子关节：围绕节点旋转，包含弹性属性，使得刚体在节点位置发生弹性偏移

BOX2D.Dynamics.Joints>>>动态关节包；
b2DistanceJoint 距离连接
b2DistanceJointDef 距离连接定义.
b2GearJoint 齿轮链接.
b2GearJointDef 齿轮连接定义.
b2Joint 连接基类.
b2JointDef 连接定义基类.
b2JointEdge 用于组合刚体或连接到一起.刚体相当于节点,而连接相当于边
b2MouseJoint 鼠标连接.
b2MouseJointDef 鼠标连接定义.
b2PrismaticJoint 移动连接.
b2PrismaticJointDef 移动连接定义.
b2PulleyJoint 滑轮连接.
b2PulleyJointDef 滑轮连接定义.
b2RevoluteJoint 旋转连接.
b2RevoluteJointDef 旋转连接定义.
————————————————
*/
class MoonBox2d {
	constructor() {
		console.log('这是一个box2dWeb的快捷创建类');
		this.miToPixel=30;//米与像素的转换
	}
	//2d世界初始化	
	box2dInit(canvas,scale=1){
		if(scale=='fullScreen'){//全屏
			canvas.width=window.innerWidth-20;
			canvas.height=window.innerHeight-20;
		}else{
			canvas.width*=scale;
			canvas.height*=scale;
		}
		this.canvas=canvas;
		this.context=canvas.getContext("2d");
		this.canvasWid=canvas.width;
		this.canvasHei=canvas.height;
		this.isPlay=true;
		this.world = new b2World(new b2Vec2(moonConfig.gravityX,moonConfig.gravity),true); //gravity  重力的方向，力度 单位牛，10就是重力加速度G
		this.createDebuger();
		this.reInit();
	}
	reInit(){
		//console.log(this.world.GetBodyCount())
		if(!this.clearTime){
			this.update();
			this.start();
			this.createWall();
		}
	}
	start(){
		this.stop();
		this.clearTime=setInterval(this.update.bind(this), 1000/60); //配置侦数
	}
	stop(){
		clearInterval(this.clearTime);
		this.clearTime=null;
	}
	//创建canvas
	createCanvas({w=940,h=590,c='#ffffff'}){
		var canvas = document.createElement('canvas');
		document.body.appendChild(canvas);
		canvas.width = w;
		canvas.height = h;
		canvas.style.backgroundColor = c;
		return canvas
	}
	//创建四边墙
	createWall(){
		var width=this.canvasWid/2/this.miToPixel*moonConfig.wallScaleX;
		var height=this.canvasHei/2/this.miToPixel*moonConfig.wallScaleY;
		var size=8/this.miToPixel;
		var fixDef = new b2FixtureDef; //刚体材质
		var world=this.world;
		fixDef.friction = moonConfig.wall.friction;   //摩擦力 加大了之后正方形被推动就比较不容易动
		fixDef.restitution = moonConfig.wall.restitution;  //弹性系数，1应该就是不衰减，大于1反而越来越快
		var bodyDef = new b2BodyDef;//刚体创建
		bodyDef.type = b2Body.b2_staticBody;
		fixDef.shape = new b2PolygonShape;
		//------
		this.wTop=createWallBody(		width,		0,			width,	size);//上
		this.wBottom=createWallBody(	width,		height*2,	width,	size);//下
		this.wLeft=createWallBody(		0, 			height,		size, 	height);//左
		this.wRight=createWallBody(		width*2, 	height,		size, 	height);//右
		function createWallBody(x,y,w,h){
			fixDef.shape.SetAsBox(w, h);
			bodyDef.position.Set(x, y);
			let body=world.CreateBody(bodyDef)
			body.CreateFixture(fixDef);
			return body;
		}
		//------
		this.background={x:0,y:0,width:width*2,height:height*2};//这里的宽高都是一半，所以要乘以2
		//console.log(this.background);
	}
	//创建形状的debuger
	createDebuger(){
		var debugDraw = new b2DebugDraw();
		debugDraw.SetSprite(this.context);
		debugDraw.SetDrawScale(this.miToPixel);
		debugDraw.SetFillAlpha(0.3);
		debugDraw.SetLineThickness(2.0);
		debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
		this.world.SetDebugDraw(debugDraw);
	}
	//更新
	update() {
		if(this.isPlay){
			var a = 1 / 60;
			this.world.Step(a,10,10);
			if(moonConfig.isShowDebuger){
				this.world.DrawDebugData();
			}else{
				if(moonConfig.isUpdateContext){
					this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
				}
			}
			this.updateUserData();
			this.updateCanvas&&this.updateCanvas();
		}
	}
	/**更新图形*/
	updateUserData(){
		let m=this.miToPixel;
		for(var b = this.world.GetBodyList(); b != null; b = b.GetNext()){
			let userData=b.GetUserData();
			if(userData){
				if(moonConfig.isUpdateContext){
					this.context.save();
					this.context.translate(b.GetPosition().x*m,b.GetPosition().y*m);
					this.context.rotate(b.GetAngle());
					this.context.drawImage(userData,-userData.width/2,-userData.height/2);
					this.context.restore(); 
				}else{
					let a=b.GetAngle()*180/Math.PI;
					[userData.x,userData.y]=[b.GetPosition().x*m,b.GetPosition().y*m];
					if(userData.hasOwnProperty('rotation'))   userData.rotation=a;
					else if(userData.hasOwnProperty('angle')) userData.angle=a;
				}
			}
		}
	}
	/**得到默认数组材质*/
	getb2FixtureDef(){
		var fixDef = new b2FixtureDef; //刚体材质
		fixDef.density = moonConfig.fixDef.density;    //密度
		fixDef.friction = moonConfig.fixDef.friction;   //摩擦力 加大了之后正方形被推动就比较不容易动
		fixDef.restitution = moonConfig.fixDef.restitution;  //弹性系数，1应该就是不衰减，大于1反而越来越快
		return fixDef;
	}
	//根据t1类型来创建刚体
	createBody({t1='rect',x=40,y=40,w=20,h=20,r=20,t2=2,arr}){
		switch(t1){
			case 'rect': 	return this.createRect(x,y,w,h,t2);
			case 'circle': 	return this.createCircle(x,y,r,t2);
			case 'polygon': return this.createPolygon(x,y,arr,t2);
		}
	}
	//创建圆形(r是圆的半径/2)
	createCircle(x,y,r,t=2){
		const miToPixel=this.miToPixel;
		const[cx,cy,cr]=[x/miToPixel,y/miToPixel,r/miToPixel];
		var fixDef = this.getb2FixtureDef();//刚体材质
		var bodyDef = new b2BodyDef;//刚体创建
		bodyDef.type = t//b2Body.b2_dynamicBody;//默认是动态
		fixDef.shape = new b2CircleShape(cr);
		bodyDef.position.Set(cx,cy);
		var body=this.world.CreateBody(bodyDef);
		console.log(bodyDef,body)
		body.CreateFixture(fixDef);
		return body;
	}
	//创建方形(w是方的宽/2,h是方的高/2)
	createRect(x,y,w,h,t=2){
		const miToPixel=this.miToPixel;
		const[cx,cy,cw,ch]=[x/miToPixel,y/miToPixel,w/miToPixel,h/miToPixel];
		var fixDef = this.getb2FixtureDef();//刚体材质
		var bodyDef = new b2BodyDef;//刚体创建
		bodyDef.type = t//b2Body.b2_dynamicBody;//默认是动态,传入b2Body.b2_staticBody为静态，0,1,2静,暂动,动
		fixDef.shape = new b2PolygonShape;
		fixDef.shape.SetAsBox(cw, ch);
		bodyDef.position.Set(cx,cy);
		var body=this.world.CreateBody(bodyDef);
		body.CreateFixture(fixDef);
		return body;
	}
	//创建多边形1,只能是正常多边形
	createPolygon(x,y,arr,t=2){
		const miToPixel=this.miToPixel;
		const[cx,cy]=[x/miToPixel,y/miToPixel];
		!arr&&(arr=this.getPolygonPoints());
		var fixDef = this.getb2FixtureDef();//刚体材质
		var bodyDef = new b2BodyDef;//刚体创建
		bodyDef.type = t//b2Body.b2_dynamicBody;//默认是动态,传入b2Body.b2_staticBody为静态，0,1,2静,暂动,动
		fixDef.shape = new b2PolygonShape;
		fixDef.shape.SetAsVector(arr,arr.length);
		bodyDef.position.Set(cx,cy);
		var body=this.world.CreateBody(bodyDef);
		body.CreateFixture(fixDef);
		return body;
	}
	//创建多边形2,n边数,r半径,a角度
	createPolygon2(x,y,n=3,r=30,a=0,t=2){
		this.createPolygon(x,y,this.getPolygonPoints(n,r,a),t)
	}
	//创建多边形3,可以创建凹凸多边形
	createPolygon3(x,y,verticesList,t=2){
		const miToPixel=this.miToPixel;
		const[cx,cy]=[x/miToPixel,y/miToPixel];
		!verticesList&&(verticesList=this.getPolygonPoints());
		var bodyDef = new b2BodyDef();
		bodyDef.type = t;
		bodyDef.position.Set(cx ,cy);
		var body=this.world.CreateBody(bodyDef);
		var fixDef = this.getb2FixtureDef();//刚体材质
		//创建一个Separator对象
		var separator = new b2Separator();
		//验证顶点是否符合创建多边形的标准
		var validate = separator.Validate(verticesList);
		//如果是顶点因非顺时针不符标准，则反转数组中的顶点顺序
		log('validate',validate,verticesList)
		if (validate == 2) {
			verticesList.reverse();
		}else if (validate != 0) {
			//如果不符合多边形标准，跳出
			return debuger;
		}
		//将顶点分解成多个凸多边形，组合成复杂的多边形
		separator.Separate(body, fixDef, verticesList);
		return body;
	}
	//得到多边的所有点数组（t边数,r半径,a角度）
	getPolygonPoints(n=3,r=30,a=0){
		let sides=[];
		let angle=0;
		r=r/-this.miToPixel;
		for (let i=0; i < n; i++) {
			sides.push(new b2Vec2((-r * Math.sin(angle+a)),(r * Math.cos(angle+a))));
			angle += 2*Math.PI/n;
		}
		return sides;
	}
	//arr需要顺时针数组,s是比例大小
	getPolygonOtherPoints(arr,s=1){
		let [sides,t]=[[],arr.length];
		for (let i=0; i < t; i++) {
			sides.push(new b2Vec2(arr[i][0]/this.miToPixel*s,arr[i][1]/this.miToPixel*s));
		}
		return sides;
	}
	//开启鼠标事件
	openMouseEvent(){
		this.canvas.addEventListener("mousedown",this.mouseDown.bind(this),true);
		this.canvas.addEventListener("mouseup",this.mouseUp.bind(this),true);
	}
	mouseDown(e){
		this.mouseX = e.offsetX / this.miToPixel;
		this.mouseY = e.offsetY / this.miToPixel;
		this.isMouseDown = true;
		document.addEventListener("mousemove", this.mouseMove.bind(this), true);
		if(this.isMouseDown && (!this.mouseJoint)) {
			var body = this.getBodyAtMouse();
			if(body) {
			   var md = new b2MouseJointDef();
			   md.bodyA = this.world.GetGroundBody();
			   md.bodyB = body;
			   md.target.Set(this.mouseX, this.mouseY);
			   md.collideConnected = true;
			   md.maxForce = moonConfig.mouseMaxForce * body.GetMass();
			   this.mouseJoint = this.world.CreateJoint(md);
			   body.SetAwake(true);
			};
		 };
	};
	mouseMove(e){
		this.mouseX = e.offsetX / this.miToPixel;
		this.mouseY = e.offsetY / this.miToPixel;
		if(this.mouseJoint) {
			if(this.isMouseDown) {
				this.mouseJoint.SetTarget(new b2Vec2(this.mouseX, this.mouseY));
		  	}
		}
	};
	mouseUp(e){
		document.removeEventListener("mousemove", this.mouseMove.bind(this), true);
		this.isMouseDown = false;
		this.mouseX = 0;
		this.mouseY = 0;
		if(this.mouseJoint){
			this.world.DestroyJoint(this.mouseJoint);
			this.mouseJoint = null;
		}
	};
	//得到鼠标点击下的刚体
	getBodyAtMouse() {
		let [mouseX,mouseY]=[this.mouseX,this.mouseY];
		this.mousePVec = new b2Vec2(mouseX, mouseY);
		var aabb = new b2AABB();
		aabb.lowerBound.Set(mouseX - 0.001, mouseY - 0.001);
		aabb.upperBound.Set(mouseX + 0.001, mouseY + 0.001);
		this.selectedBody = null;
		this.world.QueryAABB(this.getBodyCallBack.bind(this), aabb);
		return this.selectedBody;
	};
	getBodyCallBack(fixture) {
		if(fixture.GetBody().GetType() != b2Body.b2_staticBody) {
		   if(fixture.GetShape().TestPoint(fixture.GetBody().GetTransform(), this.mousePVec)) {
			  this.selectedBody = fixture.GetBody();
			  return false;
		   }
		}
		return true;
	};
	removeKeyboard(keyDownCb,keyUpCb){
		window.removeEventListener('keydown', keyDownCb,true);
		window.removeEventListener('keyup', keyUpCb,true);
	}
	addKeyboard(keyDownCb,keyUpCb){
		this.removeKeyboard(keyDownCb,keyUpCb)
		window.addEventListener('keydown', keyDownCb,true);
		window.addEventListener('keyup', keyUpCb,true);
	}
	keyToLower(s){
		return s.toLocaleLowerCase();
	}
	/**销毁所有刚体*/
	destroyAllBody(){
		let bodys=[];
		for(var b = this.world.GetBodyList(); b != null; b = b.GetNext()){
			bodys.push(b);
		}
		while(bodys.length>0){
			b=bodys.splice(0,1)[0];
			this.world.DestroyBody(b);
		}
	}
	/**销毁所有关节*/
	destroyAllJoint(){
		let joints=[];
		for(var b = this.world.GetJointList(); b != null; b = b.GetNext()){
			joints.push(b);
		}
		while(joints.length>0){
			b=joints.splice(0,1)[0];
			this.world.DestroyJoint(b);
		}
	}
	/**销毁所有 */
	destroyAll(){
		this.destroyAllJoint();
		this.destroyAllBody();
		this.stop();
	}
	getMi(val){
		return val/this.miToPixel;
	}
	//得到像素转换之后的向量
	b2Vec2(x,y){
		return new b2Vec2(x/this.miToPixel,y/this.miToPixel);
	}
	/**设置刚体角度,a为角的度数*/
	setAngle(b,a){
		b.SetAngle(a*Math.PI/180)
	}
	/**得到刚体角度*/
	getAngle(b){
		return b.GetAngle()*180/Math.PI;
	}
	/**设置刚体的3种状态  t的值分别为0:静,1:可动可静,2:动 (b2Body.b2_staticBody,b2Body.b2_kinematicBody,b2Body.b2_dynamicBody,)*/
	setType(b,t){
		b.SetType(t);
	}
	/**设置刚体位置*/
	setPosition(b,x,y){
		const[cx,cy]=[x/this.miToPixel,y/this.miToPixel];
		b.SetPosition(new b2Vec2(cx,cy))
	}
	//设置刚体x轴的线性速度
	setLineVecX(b,x){
		b.m_linearVelocity.x=x;
	}
	getLineVecX(b){
		return b.m_linearVelocity.x;
	}
	//设置刚体y轴的线性速度
	setLineVecY(b,y){
		b.m_linearVelocity.y=y;
	}
	getLineVecY(b){
		return b.m_linearVelocity.y;
	}
	//移动摄像机
	moveCamera({x,y}){
		for(var b = this.world.GetBodyList(); b != null; b = b.GetNext()){
			if(x) b.SetPosition(new b2Vec2(b.GetPosition().x+x,b.GetPosition().y));
			if(y) b.SetPosition(new b2Vec2(b.GetPosition().x,b.GetPosition().y+y));
		}
	}
	//跟踪目标{b:刚体，gx:x轴离场景左右两边距离，gy:y轴离场景上下两边距离，gx,gy相当内镜头离边界距离他们的单位是像素，里面会转单位}
	trackTarge({b,gx=100,gy=100}){
		let [x,y,gapx,gapy,wid,hei]=[b.GetPosition().x,b.GetPosition().y,this.getMi(gx),this.getMi(gy),this.getMi(this.canvasWid),this.getMi(this.canvasHei)];
		//四个方向需要触发移动的点
		let [left,top,right,bottom]=[gapx,gapy,wid-gapx,hei-gapy];
		//四面墙的四个位置
		let [xMin,xMax,yMin,yMax]=[this.wLeft.GetPosition().x,this.wRight.GetPosition().x,this.wTop.GetPosition().y,this.wBottom.GetPosition().y]
		let [vx,vy]=[0,0];
		if(x>=right){
			if(wid<xMax){
				vx=right-x;
				if(Math.abs(vx)<1){
					this.moveCamera({target:b,x:vx});
				}
			}else{
				vx=wid-xMax;
				if(vx) this.moveCamera({target:b,x:vx});
			}
		}else if(x<=left){
			if(xMin<0){
				vx=left-x;
				this.moveCamera({target:b,x:vx});
			}else{
				vx=0-xMin;
				if(vx) this.moveCamera({target:b,x:vx});
			}
		}
		if(y>=bottom){
			if(hei<yMax){
				vy=bottom-y;
				if(Math.abs(vy)<1){
					this.moveCamera({target:b,y:vy});
				}
			}else{
				vy=hei-yMax;
				if(vy)	this.moveCamera({target:b,y:vy});
				
			}
		}else if(y<=top){
			if(yMin<0){
				vy=top-y;
				this.moveCamera({target:b,y:vy});
			}else{
				vy=0-yMin;
				if(vy) this.moveCamera({target:b,y:vy});
			}
		}
	}

	//-----------------------创建各种形状的刚体-------------------

	//创建链条 n是数量,p是两个刚体间隔
	createChain({x=40,y=20,w=20,h=4,n=4,p=0}){
		//使用马达关节制作锁链
		var preBody=this.createRect(x,y,w,h);
		var anchor=new b2Vec2();
		var jointDef = new b2RevoluteJointDef();
		jointDef.fristBody=preBody;
		for (let i = 1; i <n; i++) {
			//循环创建刚体，它们的x坐标都基于节点的位置向右偏移半宽
			let _x=x+((w*2+p)*i);
			let body=this.createRect(_x,y,w,h);
			//设置节点的坐标
			anchor.Set((_x-w)/this.miToPixel,y/this.miToPixel);
			//初始化关节
			jointDef.Initialize( preBody, body, anchor);
			this.world.CreateJoint(jointDef);
			//设置preBody引用当前的刚体
			preBody = body;
		}
		return jointDef;
	}
	//创建篮子  像个凹字的篮子,s是比例的大小
	createBasket({x=100,y=100,s=2,t=2}){
		let vArray=this.getPolygonOtherPoints([[20,0],[20,-20],[24,-20],[24,4],[-24,4],[-24,-20],[-20,-20],[-20,0]],s);
		return this.createPolygon3(x,y,vArray,t);
	}
	//创建齿轮 n:齿轮的齿数(n只能3-12),r1齿轮半径，r2内圆比例，g齿轮尖部(g理想值0.03-0.1)，r1越大，可调节范围越大
	createGear({x=100,y=100,n=12,r1=50,r2=0.8,g=0.1}){
		let [sides,maxs,mins,angle,s]=[[],[],[],0,1];
		n*=2;
		for (let i=0; i <= n; ++i) {
			s=i%2==0?1:-1;
			maxs.push([(Math.sin(angle+g*s)*r1),(Math.cos(angle+g*s)*r1)]);
			mins.push([(Math.sin(angle)*r1*r2),(Math.cos(angle)*r1*r2)]);
			angle += 2*Math.PI/n;
		}
		for(let i=0;i<n;i+=2){
			sides.push(maxs[i],maxs[i+1]);
			if(i<n-2){
				sides.push(mins[i+1],mins[i+2]);
			}else{
				sides.push(mins[n-1],mins[0]);
			}		
		}
		let vArray=this.getPolygonOtherPoints(sides)
		return this.createPolygon3(x,y,vArray);
	}
}
/**创建凹凸多边形类*/
class b2Separator
{
	constructor()
	{
	}
	/**
	 Separator方法用来将verticeVec中的顶点分成多个小的凸多边形，并赋值给fixtrueDef，创建body多边形刚体。
	每个参数说明如下：
	body：多边形刚体对象
	fixtureDef：fixture需求对象
	verticeVec：存储多边形所顶点的Vector数组
	scale：Box2D模拟时的缩放比例，这个值与b2DebugDraw中的SetDrawScale的参数值是一样的。
	 * */
	Separate(body, fixtureDef, verticesVec, scale = 30)
	{			
		var i, n = verticesVec.length, j, m;
		var vec = [], figsVec = [];
		var polyShape;
		for(i=0; i<n; i++) vec.push(new b2Vec2(verticesVec[i].x*scale, verticesVec[i].y*scale));
		//log('n',n);
		figsVec = this.calcShapes(vec);
		n = figsVec.length;	
		for(i=0; i<n; i++)
		{
			verticesVec = [];
			vec = figsVec[i];
			m = vec.length;
			for(j=0; j<m; j++) verticesVec.push(new b2Vec2(vec[j].x/scale, vec[j].y/scale));
			//log('m',verticesVec);
			polyShape = new b2PolygonShape();
			polyShape.SetAsVector(verticesVec);
			fixtureDef.shape = polyShape;
			body.CreateFixture(fixtureDef);
		}
	}
	/**
		Validate的功能是检测多边形的刚体是否符合创建的标准，在不符合标准时，返回不符合标准的原因。
		每个参数说明如下：
		verticeVec：存储多边形所顶点的Vector数组
		返回值类型有下面几种：
		0：顶点符合绘制多边形的标准
		1：顶点连接的线段之间有交叉
		2：顶点的顺序非顺时针绘制
		3：出现1和2两种错误 
	 * */
	Validate(verticesVec)
	{
		var i, n = verticesVec.length, j, j2, i2, i3, d, ret = 0;
		var fl = false, fl2 = false;
		for(i=0; i<n; i++)
		{
			i2 = (i<n-1?i+1:0);
			i3 = (i>0?i-1:n-1);
			fl = false;
			for(j=0; j<n; j++)
			{
				if(j!=i&&j!=i2)
				{
					if(!fl)
					{
						d = this.det(verticesVec[i].x, verticesVec[i].y, verticesVec[i2].x, verticesVec[i2].y, verticesVec[j].x, verticesVec[j].y);
						if(d>0) fl = true;
					}
					if(j!=i3)
					{
						j2 = (j<n-1?j+1:0);
						if(this.hitSegment(verticesVec[i].x, verticesVec[i].y, verticesVec[i2].x, verticesVec[i2].y, verticesVec[j].x, verticesVec[j].y, verticesVec[j2].x, verticesVec[j2].y))
							ret = 1;
					}
				}
			}
			if(!fl) fl2 = true;			
		}
		if(fl2)
		{
			if(ret==1) ret = 3;
			else ret = 2;
		}
		return ret;
	}
	calcShapes(verticesVec)
	{
		var vec=[] ;
		var i, n, j;
		var d, t, dx, dy, minLen;
		var i1, i2, i3, p1, p2, p3;
		var j1, j2, v1, v2, k, h;
		var vec1,vec2;
		var v, hitV;
		var isConvex;
		var figsVec = [], queue = [];
		queue.push(verticesVec);
		while(queue.length)
		{
			vec = queue[0];
			n = vec.length;
			isConvex = true;
			for(i=0; i<n; i++)
			{
				i1 = i;
				i2 = (i<n-1?i+1:i+1-n);
				i3 = (i<n-2?i+2:i+2-n);
				p1 = vec[i1];
				p2 = vec[i2];
				p3 = vec[i3];
				d = this.det(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
				if(d<0)
				{
					isConvex = false;
					minLen = Number.MAX_VALUE;
					for(j=0; j<n; j++)
					{
						if(j!=i1&&j!=i2)
						{
							j1 = j;
							j2 = (j<n-1?j+1:0);
							v1 = vec[j1];
							v2 = vec[j2];
							v = this.hitRay(p1.x, p1.y, p2.x, p2.y, v1.x, v1.y, v2.x, v2.y);
							if(v)
							{
								dx = p2.x-v.x;
								dy = p2.y-v.y;
								t = dx*dx+dy*dy;
								if(t<minLen)
								{
									h = j1;
									k = j2;
									hitV = v;
									minLen = t;
								}
							}
						}
					}
					if(minLen==Number.MAX_VALUE) this.err();
					vec1 = [];
					vec2 = [];
					j1 = h;
					j2 = k;
					v1 = vec[j1];
					v2 = vec[j2];
					if(!this.pointsMatch(hitV.x, hitV.y, v2.x, v2.y)) vec1.push(hitV);
					if(!this.pointsMatch(hitV.x, hitV.y, v1.x, v1.y)) vec2.push(hitV);					
					h = -1;
					k = i1;
					while(true)
					{
						if(k!=j2) vec1.push(vec[k]);
						else
						{
							if(h<0||h>=n) this.err();
							if(!this.isOnSegment(v2.x, v2.y, vec[h].x, vec[h].y, p1.x, p1.y)) vec1.push(vec[k]);
							break;
						}

						h = k;
						if(k-1<0) k = n-1;
						else k--;
					}
					vec1 = vec1.reverse();
					h = -1;
					k = i2;
					while(true)
					{
						if(k!=j1) vec2.push(vec[k]);
						else
						{
							if(h<0||h>=n) this.err();
							if(k==j1&&!this.isOnSegment(v1.x, v1.y, vec[h].x, vec[h].y, p2.x, p2.y)) vec2.push(vec[k]);
							break;
						}
						h = k;
						if(k+1>n-1) k = 0;
						else k++;
					}
					queue.push(vec1, vec2);
					queue.shift();
					break;
				}
			}
			if(isConvex) figsVec.push(queue.shift());
		}
		return figsVec;
	}
	hitRay(x1, y1, x2, y2, x3, y3, x4, y4)
	{
		var t1 = x3-x1, t2 = y3-y1, t3 = x2-x1, t4 = y2-y1, 
			t5 = x4-x3, t6 = y4-y3, t7 = t4*t5-t3*t6, a;
		
		a = (t5*t2-t6*t1)/t7;
		var px = x1+a*t3, py = y1+a*t4;
		var b1 = this.isOnSegment(x2, y2, x1, y1, px, py);
		var b2 = this.isOnSegment(px, py, x3, y3, x4, y4);
		if(b1&&b2) return new b2Vec2(px, py);
		return null;
	}
	hitSegment(x1, y1, x2, y2, x3, y3, x4, y4)
	{
		var t1 = x3-x1, t2 = y3-y1, t3 = x2-x1, t4 = y2-y1, 
			t5 = x4-x3, t6 = y4-y3, t7 = t4*t5-t3*t6, a;
		a = (t5*t2-t6*t1)/t7;
		var px = x1+a*t3, py = y1+a*t4;
		var b1 = this.isOnSegment(px, py, x1, y1, x2, y2);
		var b2 = this.isOnSegment(px, py, x3, y3, x4, y4);
		if(b1&&b2) return new b2Vec2(px, py);
		return null;
	}
	isOnSegment(px, py, x1, y1, x2, y2)
	{
		var b1 = ((x1+0.1>=px&&px>=x2-0.1)||(x1-0.1<=px&&px<=x2+0.1));
		var b2 = ((y1+0.1>=py&&py>=y2-0.1)||(y1-0.1<=py&&py<=y2+0.1));
		return (b1&&b2&&this.isOnLine(px, py, x1, y1, x2, y2));
	}
	pointsMatch(x1, y1, x2, y2)
	{
		var dx = (x2>=x1?x2-x1:x1-x2), dy = (y2>=y1?y2-y1:y1-y2);
		return (dx<0.1&&dy<0.1);
	}
	isOnLine(px, py, x1, y1, x2, y2)
	{
		if(x2-x1>0.1||x1-x2>0.1)
		{
			var a = (y2-y1)/(x2-x1), possibleY = a*(px-x1)+y1, diff = (possibleY>py?possibleY-py:py-possibleY);
			return (diff<0.1);
		}
		return (px-x1<0.1||x1-px<0.1);			
	}
	det(x1, y1, x2, y2, x3, y3)
	{
		return x1*y2+x2*y3+x3*y1-y1*x2-y2*x3-y3*x1;    
	}
	err()
	{
		throw new Error("A problem has occurred. Use the Validate() method to see where the problem is.");
	}
}